Sync addons-source@maintenance/gramps60 with upstream (2026-05-30)#34
Closed
eduralph wants to merge 15 commits into
Closed
Sync addons-source@maintenance/gramps60 with upstream (2026-05-30)#34eduralph wants to merge 15 commits into
eduralph wants to merge 15 commits into
Conversation
Introduce GitHub Actions CI inside a shared Docker image on ghcr.io, plus a native Windows runner for cross-platform unit-test coverage, and a shared unittest harness with Gramps-backed fixtures that verifies every addon registers, loads, and exposes valid plugin metadata. CI infrastructure ----------------- - .github/docker/gramps-ci/Dockerfile — Python 3.12 + Gramps 6.0 (pip) + PyGObject + GTK typelibs + xvfb/xauth + ruff, dbf, intltool, gettext, git. GTK lives in the base so addon modules that do `from gi.repository import Gtk` at load time are importable; xvfb and xauth are bundled for tests that actually render. - .github/workflows/docker-build.yml — rebuilds the image on .github/docker/** changes or via workflow_dispatch. - .github/workflows/ci.yml — seven jobs: lint (ruff E9/F63/F7/F82 + trailing whitespace), addon-structure (every addon has po/template.pot), compile-check (py_compile on every .py), unit-test-linux (container), unit-test-windows (native, conda+pip), integration-test (container with --init so xvfb-run does not hang), build (make.py gramps60 build all). - .github/environment.yml — hybrid conda+pip env for Windows. Gramps is not on conda-forge, so pygobject/gtk3 come from conda and gramps/orjson/dbf come from pip. Shared test harness ------------------- - tests/__init__.py — GPL header. - tests/gramps_test_env.py — sys.path / GRAMPS_RESOURCES bootstrap and two unittest base classes: GrampsTestCase (session-cached plugin manager + registry via setUpClass) and GrampsDbTestCase (same plus a fresh in-memory SQLite DB per test). - tests/test_plugin_registration.py — four unittest.TestCase classes covering plugin registration, subprocess-isolated module loading (crash-safe), required metadata (gramps_target_version=6.0, valid id/name/version), and import/export entry-function smoke tests. Gate policy ----------- All seven jobs run on every push and PR. Four are marked continue-on-error: true so they surface issues without blocking merges while the existing tree is cleaned up: - lint (~79 pre-existing ruff E9/F63/F7/F82 errors) - addon-structure (4 addons missing po/template.pot) - unit-test-linux (some addon test modules fail to import today) - unit-test-windows (same) compile-check, integration-test, and build are blocking from day one. Each non-blocking gate will be flipped to blocking in the same follow-up PR that fixes its underlying issues, so the tightening is incremental and visible in history. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…R 820 (#12) * CI: add shell: bash to unit-test-linux + integration-test steps Root cause of "Unit Tests (Linux)" and "Integration Tests (Gramps)" failures was not broken test modules — the steps never invoked unittest. The container's default shell is /bin/sh (dash on python:3.12-slim), and the inline scripts use bash-only parameter expansions (${f%.py}, ${mod//\//.}) to build the dotted module list. Dash fails with "Bad substitution" on the first such line; the rest of the script never runs. continue-on-error: true masked this as a generic job failure for two CI rounds. Add "shell: bash" explicitly to: - unit-test-linux / Run per-addon unit tests (bashisms) - integration-test / Run per-addon integration tests (bashisms) - integration-test / Run plugin registration tests (no bashisms today, but consistent and future-proof) Compile Check already sets shell: bash. Windows jobs inherit bash via defaults.run at the job level. No other steps affected. * CI: split OS-specific addon tests via filename convention The Windows unit-test job hung on TMGimporter's DB-backed tests because make_database("sqlite").load(":memory:", None) deadlocks under the conda-forge GTK + pip Gramps combination. Rather than patch the hang, introduce a filename convention so per-addon authors can declare OS scope up front: test_*.py general (every OS) test_linux_*.py Linux-only test_windows_*.py Windows-only test_integration_*.py Linux-only, full-pipeline/DB-backed (pre-existing) unit-test-linux skips test_windows_* and test_integration_*; unit-test-windows skips test_linux_* and test_integration_*. Applied to TMGimporter: the 13 DB-backed classes in tests/test_libtmg.py move to tests/test_linux_libtmg.py (along with the _Rec/_table/_make_db/ _add_person/_MockUser helpers they use). The 7 pure-logic classes (TestStripTmgCodes, TestTmgDateToGrampsDate, TestNumTo{Month,Date}, TestParseDate, TestRepoTypeFromName, TestUrlFromName) stay in test_libtmg.py and will run on every OS. Locally all 175 tests still pass via run-addon-unit.sh TMGimporter.
* Wrap label vaue to ensure it is a string The argument `label` to Gtk.Label must be of type string. In recent versions of Python and/or PyGObject, this check has become stricter and as a result is throwing an exception instead of silently converting to a string. So now we explicitly convert to string. Fixes #14181 * TimelinePedigreeView: fix crash on second right-click in context menu (bug 0012387) The settings submenu builder appended the same SeparatorMenuItem twice, which GTK rejected with "Can't set a parent on widget which has a parent". The corrupt parent linkage caused a segfault when the previous menu was garbage-collected on the next right-click. Removing the stray duplicate append fixes all four context-menu paths (background canvas, person, relation, missing parent) and silences the related GTK_IS_WIDGET assertion warnings. Also resolves duplicate report 0013463. * Merge TimelinePedigree PR 819, 823 * DataEntryGramplet: fix crash when adding person with no Family Tree open (bug 0012691) Clicking Add (or Save after a dirty edit) when no tree was loaded raised AttributeError: 'DummyDb' object has no attribute 'get_undodb' from DbTxn. Guard both mutating callbacks on dbstate.is_open() and surface a clear ErrorDialog instead. Add unit tests for the closed-db guards, pre-existing input guards, and .gpr.py registration metadata so future refactors can't silently break the bug-12691 fix. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * DataEntryGramplet: convert tests to stdlib unittest (bug 0012691) Gramps' own test suite uses unittest, not pytest, so addon tests that ship alongside the codebase should follow the same convention to stay contributable upstream without rewriting. Replace pytest fixtures, monkeypatch, and module-level test functions with unittest.TestCase classes and mock.patch.object. Guard the module-level addon import with a try/except that raises SkipTest so collection is quiet on environments without the GUI stack, and pin Gtk to 3.0 before gramps imports to avoid the GTK4 fallback crash on Gtk.IconSize.MENU. * DataEntryGramplet: qualify test import to pick up the class not the module When unittest loads this file as DataEntryGramplet.tests.test_..., the outer DataEntryGramplet is already a namespace package in sys.modules, so `from DataEntryGramplet import DataEntryGramplet` binds the submodule and DataEntryGramplet.NO_REL (class attr) raises AttributeError. Fix by importing the class via its fully-qualified path. * Merge DataEntryGramplet: fix crash when adding person with no Family Tree open (bug 0012691) gramps-project#824 * CalculateEstimatedDates: handle ancestry-loop DatabaseError per-person (bug 0007898) probably_alive_range raises DatabaseError when it detects loops in ancestor or descendant chains. Previously this propagated out of the removal, selection, and apply loops and tore down the entire tool, leaving signals disabled and the progress dialog stuck open. Wrap each per-person iteration with try/except so a single bad record is logged and skipped, and add outer try/finally blocks so signals are re-enabled and the progress dialog is closed even on unexpected failures. Surface a "Skipped N people due to errors" message to the user when any rows were skipped. Add unit tests covering get_modifier branches, calc_estimates happy path, DatabaseError propagation from calc_estimates, and .gpr.py registration metadata. The addon module is loaded lazily inside a fixture so pytest collection succeeds even when the GUI stack cannot import. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * CalculateEstimatedDates: convert tests to stdlib unittest (bug 0007898) Gramps' own test suite uses unittest, not pytest, so addon tests that ship alongside the codebase should follow the same convention to stay contributable upstream without rewriting. Replace pytest fixtures, monkeypatch, and pytest.raises with unittest.TestCase, mock.patch.object, and assertRaisesRegex. Guard the module-level addon import with a try/except that raises SkipTest so collection is quiet on environments without the GUI stack, and pin Gtk to 3.0 before gramps imports to avoid the GTK4 fallback crash on Gtk.IconSize.MENU. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Merge CalculateEstimatedDates: handle ancestry-loop DatabaseError (bug 0007898)gramps-project#825 * ImportMerge: fix AttributeError when adding/merging Tag objects Tag is a table object without a gramps_id field, so the generic has_<obj_type>_gramps_id / find_next_<obj_type>_gramps_id lookups in do_commits raised AttributeError when the user selected Add on a Tag row. Guard both the S_ADD and S_DIFFERS GID-conflict blocks so they skip Tag. Adds integration tests covering both branches; verified they fail without the guard and pass with it. Fixes bug 0014056 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ImportMerge: pin Gtk to 3.0 in integration test On systems with both GTK3 and GTK4 installed, PyGObject defaults to GTK4, and importing ImportMerge pulls in gramps.gui which crashes on Gtk.IconSize.MENU (a GTK3-only enum). Mirror the pin that gramps.grampsapp performs at startup so reviewers can run the test without environment tweaks. * ImportMerge: rewrite integration test with unittest framework AGENTS.md requires the unittest framework for tests. Convert the pytest-based integration test (fixtures + assert statements) to a unittest.TestCase with setUp/tearDown and self.assert* calls. GTK availability is now checked with a module-level try/except raising unittest.SkipTest, which also makes the separate GTK-pin step redundant (still applied here before the gramps import). * ImportMerge: apply Black formatting to integration test Addresses AGENTS.md rule requiring Black-formatted Python. Collapses three multi-line function calls that fit on a single line. * ImportMerge: add type hints and class header to integration test Addresses AGENTS.md requirements: - Type hints on all helpers and test methods using Python 3.10+ syntax (``X | None``, ``tuple[X, Y]``). - Sphinx-style ``:param:`` / ``:returns:`` docstring markers on the helper functions. - ``# ------`` class-header divider above the TestCase so it's easy to locate. No behavioural change. * Merge ImportMerge: fix AttributeError when adding/merging Tag objects (bug 0014056) gramps-project#826 * Form: fix crash and surface clear errors for malformed XML (bug 0011707) A family section whose title lacked the expected 'X/Y' separator caused a ValueError: not enough values to unpack when the form editor opened, crashing the Forms gramplet. The underlying issue was that the addon trusted the XML definitions and had no schema validation or user-facing error reporting for broken files. Split the validation out of form.py into a pure-Python form_validator module (no GTK/Gramps imports) so it can be unit-tested without a GUI. The Form loader now: * parses each file defensively (ExpatError -> ErrorDialog), * runs the validator before loading (invalid files -> ErrorDialog with the file path, offending form id, and the rule that failed), * skips any <form> element that fails validation while still loading sibling well-formed forms from the same file. split_family_title() in form_validator belt-and-braces the FamilySection constructor so a missing separator no longer raises, even if validation is bypassed. Also adds diagnostic logging: * INFO log of forms loaded per file, * DEBUG trace of each file parsed and each form id loaded/skipped, * DEBUG when EditForm opens (event/citation handles), * WARNING in FamilySection if its title lacks 'X/Y'. Tests: * Form/tests/test_form_validator.py -- 32 pure-Python unit tests, covers split_family_title, every validation branch, parse_and_validate file handling, and a sanity check that every shipped form_*.xml passes validation. * Form/tests/test_integration_form.py -- unittest integration tests that patch ErrorDialog to verify the loader surfaces syntax errors, invalid family titles, missing role, and invalid section types; partially broken files still load their valid forms; shipped files trigger no dialogs. Partially addresses bug 0011010 (request for user error dialog for unsupported elements) by covering its core ask: clear errors for invalid section types, missing/empty role, missing/empty type, and XML syntax errors. Fixes #11707. Refs #11010. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Form: detect empty definition files and warn on column-size mismatches (bug 0011010) A form definition file whose <forms> root contained no <form> elements used to load silently — the loader iterated zero <form> children and returned, leaving the user with no feedback. validate_form_dom now reports this as an error so the ErrorDialog wiring added for bug 11707 surfaces the problem on load. get_form_warnings is a new non-fatal check for sections whose <column> <size> values do not sum to 100. 78 shipped definition files violate this rule today without breaking rendering (the size field is parsed but never read by the layout code), so treating it as an error would flag correctly-working forms. The loader logs the warnings via LOG.warning instead, making authoring mistakes in user-authored custom.xml files diagnosable without harassing users of the built-in forms. Unit coverage: rejected empty <forms>, forms-root-with-only-comments, and every branch of the sized-column check (no columns, no sizes, summing to 100, summing to other totals, partial sizing, independent of errors, multiple sections). Integration coverage: empty-forms triggers ErrorDialog; size mismatch is logged but does not block the form from loading. Tests use stdlib unittest to match Gramps' own conventions. * Merge Form: gramps-project#821 and (bug 11010)gramps-project#822 to 6.1 branch * WebSearch: fix bare imports in test_filetable for dotted-path loading `WebSearch/tests/test_filetable.py` imports `models`, `constants` and `db_file_table` without a package prefix. Those resolve only when `WebSearch/` itself is on sys.path — i.e. when the test is loaded via `unittest discover` from inside `tests/`. Under the dotted-path form that addons-source's own ci.yml uses (`python3 -m unittest WebSearch.tests.test_filetable` from the addons-source root), the imports look for a top-level `models` module and the test fails to load: ImportError: Failed to import test module: test_filetable ModuleNotFoundError: No module named 'models' Add the same `sys.path.insert(0, …parent dir…)` prologue that TMGimporter and Form already use for the same pattern, so the test loads under either form. No behavioural change beyond the imports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Merge WebSearch: fix bare imports in test_filetable for dotted-path loading gramps-project#833 --------- Co-authored-by: Himanshu Gohel <1551217+hgohel@users.noreply.github.com> Co-authored-by: GaryGriffin <genealogy@garygriffin.net> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The Dockerfile bakes in only `dbf`, but addons declare a wider set of Python deps in their .gpr.py `requires_mod` lists (networkx, psycopg2, pygraphviz, lxml, svgwrite, boto3, litellm, life_line_chart, psycopg). Without these installed, per-addon unit tests and the plugin- registration subprocess load fail with ImportError/NameError. Add a pre-test step to unit-test-linux, unit-test-windows, and integration-test that globs every *.gpr.py, extracts the requires_mod union via ast.literal_eval, and pip-installs each package one at a time. Per-package install (not batched) keeps a single build failure (pygraphviz without graphviz-dev, psycopg2 without libpq-dev) from aborting the rest — the affected addon's tests will skip or fail in isolation without blocking others. Mirrors Gramps' Addon Manager install path (gramps/gui/plug/_windows.py __on_install_clicked → req.install → gen/utils/requirements.py), keeping .gpr.py files as the single source of truth for addon deps. New addon deps do not need a parallel update to the Dockerfile or this workflow.
`QuiltView/QuiltView.py:1266` constructs a fresh `Surname()` to
attach to a new `Name` when adding a child via the editor, but
`Surname` is never imported. Ruff (F821) flags it:
F821 Undefined name `Surname`
--> QuiltView/QuiltView.py:1266:26
The file already imports `Person, Family, ChildRef, Name` from
`gramps.gen.lib` — extend that import to include `Surname`. Without
it, the add-child path raises `NameError` instead of opening the
editor.
This PR addresses only the `Surname` F821. Two other F821 sites in
the same file (`displayer` on lines 1239/1241, where the call should
likely be `name_displayer.display(...)` rather than a top-level
`displayer` reference) are real-bug fixes and are deliberately left
out of this surgical lint PR — they need a small behavioural review
of their own.
Verified via `ruff check --select=E9,F63,F7,F82 --no-fix
--exclude='*.gpr.py' QuiltView/` (Surname F821 removed; the two
unrelated `displayer` F821s remain) and `python3 -m py_compile`.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…c/upstream-into-fork-maint-gramps60 # Conflicts: # Form/CensusCheckQuickview.gpr.py # Form/formgramplet.gpr.py # TimelinePedigreeView/TimelinePedigreeView.gpr.py
…amps60 Sync fork's maintenance/gramps60 with upstream (2026-05-17)
…60-auto Sync addons-source@maintenance/gramps60 with upstream (2026-05-17)
…60-auto Sync addons-source@maintenance/gramps60 with upstream (2026-05-19)
…60-auto Sync addons-source@maintenance/gramps60 with upstream (2026-05-27)
Bring maintenance/gramps60's .github/ up to feature/ci-cd-pipeline-upstream (the 820 PR head, which is gramps60-based), same forward-port as gramps61. gramps60 was further behind — also updates the gramps-ci Dockerfile and docker-build.yml in addition to the run_addon_tests.py harness, addon_system_deps.py, gi_bootstrap, and ci.yml wiring. Branch-neutral ci.yml unchanged in intent. Forward-port only; no addon changes.
e5884fe to
947acbf
Compare
947acbf to
5a398c3
Compare
3685448 to
779a41b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated nightly sync from
gramps-project/addons-source@maintenance/gramps60. Generated by .github/workflows/upstream-sync.yml on the testbed.